#include <iostream>
#include <string>
#include "modulos/test/test.h"
#include "Tipos.h"
#include "DiccTrie.h"
#include "ArbolCategorias.h"
#include "LinkLinkIt.h"
#include "Driver.h"

/**
 * @mainpage
 *
 * Esta es la implementación en C++ del trabajo práctico 2.
 *
 * @section autores Autores
 * <table>
 * <caption>Grupo 26</caption>
 * <tr><th>Nombre</th><th>LU</th></tr>
 * <tr><td>Aguerre, Ezequiel R.</td><td>246/07</td></tr>
 * <tr><td>Bender, Mauro E.</td><td>623/07</td></tr>
 * <tr><td>Zárate, Eduardo A.</td><td>587/02</td></tr>
 * </table>
 *
 * @section clases Clases
 * La implementación realizada por nosotros se compone de las siguientes 3 clases:
 * @li DiccTrie: Implementación de un diccionario basado en trie.
 * @li ArbolCategorias: Implementación del árbol de categorías.
 * @li LinkLinkIt: Implementación del sistema.
 *
 * @section archivos Archivos
 * Estas clases se definen en los siguientes 3 archivos:
 * @li DiccTrie.h
 * @li ArbolCategorias.h
 * @li LinkLinkIt.h
 *
 * @section tests Tests
 * 
 * @subsection testdicctrie DiccTrie
 * 
 * Se verificó el correcto funcionamiento de las operaciones básicas del
 * diccionario, como crear el diccionario, definir una nueva clave o reemplazar
 * una existente, verificar si cierta clave está definida y obtener el significado
 * de las claves definidas.
 * 
 * Para las pruebas se usaron algunas claves que contenían prefijos en común o que eran
 * prefijos unas de otras. Esto nos sirvió para asegurarnos que entre algunas
 * de ellas se compartían nodos dentro del trie, y que luego, al destruir
 * el diccionario, estos nodos eran destruidos de forma correcta.
 * 
 * Al momento de verificar las claves definidas en el diccinario probamos, además
 * de que las claves definidas por nosotros estén efectivamente definidas, que
 * claves que son prefijos de éstas no lo estén.
 * Con esto verificamos que al momento de definir una nueva clave sólo se marquen
 * como definidos los nodos que se corresponden con dichas claves y no los
 * nodos intermedios que son creados para poder guardarlas en el árbol.
 * 
 * @subsection testac ArbolCategorias
 * 
 * Al igual que con DiccTrie, en ArbolCategorias se crearon tests para verificar
 * el correcto funcionamiento de todas sus operaciones básicas. Para llevar esto
 * a cabo se creo un árbol con una jerarquía arbitraria de categorías y luego se
 * procedió a verficar que dicha jerarquía fuera repestada. Para esto, una vez 
 * creado el árbol, se verificaron los padres de cada una de las categorías
 * agregadas y también que los ids de cada una de ellas hayan sido asignados
 * de forma correcta.
 * 
 * En el caso del iterador se verificó que los tres contructores que se disponen
 * funcionen de forma correcta y que una vez creado el iterador, éste itere
 * de forma correcta sobre las categorías.
 * 
 * El primer constructor que se verificó fue el constructor por defecto. Éste
 * constructor sólo recibe como parámetro el árbol de categorías e itera sobre
 * las categorías hijas de la raíz de dicho árbol. En este caso se verificó
 * que las categorías devueltas por el iterador sean efectivamente las hijas
 * de la raíz y que los datos que proyectaba el iterador en cada iteración
 * fueran correctos.
 * 
 * El segundo constructor que se verificó fue el que, además de recibir como
 * parámetro al árbol de categorías, recibe la categoría sobre cuyas categorías hijas
 * se quiere iterar. En este caso el testeo se llevó a cabo pasandole como
 * segundo parámetro la categoría raíz del árbol y verificando que los resultados
 * obtenidos al iterar sean iguales al caso anterior.
 * 
 * Por último se verificó el funcionamiento del iterador que se construye
 * a partir de otro iterador. Este iterador se encarga de iterar sobre las
 * categorías hijas de la categoría actual del iterador pasado como parámetro.
 * En este caso se pasó como parámetro un iterador recién creado por lo que
 * la categorías sobre las que se iteró eran las hijas de la primer hija de
 * la raíz ("nivel1.1"). Como en los casos anteriores, se verificó que efectivamente
 * se iterara sobre las hijas de dicha categoría y que los datos que proyectaba
 * el iterador en cada iteración sean los correctos.
 * 
 * 
 * @subsection testlli LinkLinkIt
 *
 * Se crearon tests para verificar constructores, destructores y otras operaciones de LinkLinkIt.
 * 
 * Para verificar el correcto funcionamiento del iterador de linklinkit
 * creamos funciones de testeo por cada categoría, verificando por cada una de
 * ellas que el iterador recorra todos sus links y además que estén ordenados
 * por sus accesos recientes. Para los links con la misma cantidad de accesos
 * recientes no se pide ningún orden en particular. Además, por cada link iterado,
 * se verifica que la categoría a la que pertenezca sea la correcta.
 *
 * @section dificultades Dificultades
 * @subsection dicctrie DiccTrie
 * En el TP2 diseñamos a DiccTrie para que guarde una referencia a los significados, pero nos encontramos con un problema
 * al implementarlo en C++, ya que este lenguaje tiene una semántica diferente para las referencias que el lenguaje de
 * diseño. En C++ las referencias son inmutables, por lo que no podíamos crear un nodo con una referencia inválida y
 * luego modificar esta referencia.
 *
 * La solución consistió en guardar un puntero a la referencia que se nos estaba pasando, pero esto no solucionó los problemas,
 * ya que en C++ (a diferencia del lenguaje de diseño) un dato no existe mientras haya referencias a él, sino que pueden existir
 * referencias que ya no sirvan. Esto provocó que ArbolCategorias no funcionase, ya que al agregar un elemento al diccionario se
 * hacía de esta manera:
 *
 * @verbatim
Nodo *nuevoNodo = new Nodo();
diccionario.Definir( categoria, nuevoNodo );
@endverbatim
 *
 * El problema radicaba en que se guardaba un puntero a nuevoNodo, pero nuevoNodo dejaba de existir al salir de la función, con lo
 * cual terminábamos guardando un dato inválido. La solución definitiva fue guardar una copia del significado, en lugar de una
 * referencia, lo cual, por suerte, no modifica la complejidad de nada, pues sólo guardamos punteros en los diccionarios, y copiar
 * un puntero es @f$\Theta(1)@f$.
 *
 * Otra modificación que hicimos fue, en lugar de guardar un elemento de tipo T por cada nodo, guardar un puntero a un elemento de tipo T,
 * de esa manera se evita la necesidad de un constructor por defecto para el tipo T. Ahora sólo requiere un constructor por copia.
 *
 * Por último, como DiccTrie no posee iteradores (no se diseñaron porque no eran necesarios) se ideó un método para no perder memoria
 * y permitirle al usuario del diccionario destruir elementos. ¿Por qué? Porque nosotros guardamos punteros a nodos en el diccionario,
 * pero al momento de destruir el diccionario los nodos a los que apuntan estos punteros no se destruyen. En este punto había varias opciones:
 * @li El usuario del diccionario guarda (en otro lugar, por ej. un arreglo o una lista) copias de estos punteros a nodos, y al momento
 *     de desturir el diccionario recorre estas copias borrándolas una por una.
 * @li Crear un iterador de DiccTrie para que el usuario pueda recorrer los elementos y borrar los nodos uno por uno.
 * @li Utilizar algún tipo de puntero inteligente que posea un destructor en el cual se libere la memoria pedida.
 * @li Utilizar una función definida por el usuario para borrar los nodos en los momentos adecuados.
 *
 * Elegimos la última opción simplemente porque es la manera más fácil y que requiere menos modificaciones sobre el diseño inicial.
 *
 * @subsection arbolcategorias ArbolCategorias
 * No tuvimos mayores problemas con ArbolCategorias, simplemente hubo que realizar algunos cambios en la forma de construir las
 * "tuplas" que devuelve el iterador debido a que las referencias en C++ se tienen que inicializar sí o sí.
 *
 * @subsection linklinkit LinkLinkIt
 * Cambiamos la forma de calcular las fechas de acuerdo a la especificación del TP2:
 * @li <a href="http://code.google.com/p/algo2-2012/source/detail?r=6388d4e3306505e5464fc43d2cdc96ccbf19b437">commit 6388d4e3306505e5464fc43d2cdc96ccbf19b437</a>.
 * @li <a href="http://code.google.com/p/algo2-2012/source/detail?r=9e3ade5b51607e0e2be08d173fefd129242490a5">commit 9e3ade5b51607e0e2be08d173fefd129242490a5</a>
 * @li <a href="http://code.google.com/p/algo2-2012/source/detail?r=12b5f151383354b8489ca8bcd05938c88719b65c">commit 12b5f151383354b8489ca8bcd05938c88719b65c</a>
 *
 * Al realizar esta serie de modificaciones se cambió la complejidad de LinkLinkIt::Acceso(), de
 * @f$\Theta(|link|+altura(categorias(lli)))@f$ a @f$\Theta(|link|)@f$.
 */

/* DiccTrie */
void DiccTrieConstructor( std::ostream & ) {
	DiccTrie<std::string> dicc;
}

void __diccdes( std::string *&s ) { delete s; }
void DiccTrieDestructor( std::ostream & ) {
	DiccTrie<std::string *> dicc;

	dicc.EstablecerDestructor( __diccdes );
	dicc.Definir( "Hola", new std::string( "Significado de Hola" ) );
	dicc.Definir( "Chau", new std::string( "Significado de Chau" ) );
	dicc.Definir( "Holavision", new std::string( "Significado de Holavision" ) );
	dicc.Definir( "ChaChaCha", new std::string( "Significado de ChaChaCha" ) );
}

void DiccTrieCrear( DiccTrie<std::string> &dicc ) {
	dicc.Definir( "Nombre", "Pinocho" );
	dicc.Definir( "Apellido", "Madera" );
	dicc.Definir( "DNI", "98.123.543" );
	dicc.Definir( "Dirección", "Av. Palo 123" );
	dicc.Definir( "Teléfono fijo", "011-4444-4444" );
	dicc.Definir( "Teléfono móvil", "011-15-4444-4444" );
	dicc.Definir( "Nomb", "Pinocho!!" );
}

void DiccTrieDefinir( std::ostream & ) {
	DiccTrie<std::string> dicc;
	DiccTrieCrear( dicc );
}

void DiccTrieDefinido( std::ostream & ) {
	DiccTrie<std::string> dicc;
	DiccTrieCrear( dicc );
	AFIRMAR( dicc.Definido( "Nombre" ) );
	AFIRMAR( dicc.Definido( "Apellido" ) );
	AFIRMAR( dicc.Definido( "DNI" ) );
	AFIRMAR( dicc.Definido( "Dirección" ) );
	AFIRMAR( dicc.Definido( "Teléfono fijo" ) );
	AFIRMAR( dicc.Definido( "Teléfono móvil" ) );
	AFIRMAR( dicc.Definido( "Nomb" ) );
	NEGAR( dicc.Definido( "Nombres" ) );
	NEGAR( dicc.Definido( "Nom" ) );
	NEGAR( dicc.Definido( "Nombr" ) );
	NEGAR( dicc.Definido( "Teléfono " ) );
}

void DiccTrieObtener( std::ostream & ) {
	DiccTrie<std::string> dicc;
	DiccTrieCrear( dicc );
	ASEGURAR( dicc.Obtener( "Nombre" ), "Pinocho" );
	ASEGURAR( dicc.Obtener( "Apellido" ), "Madera" );
	ASEGURAR( dicc.Obtener( "DNI" ), "98.123.543" );
	ASEGURAR( dicc.Obtener( "Dirección" ), "Av. Palo 123" );
	ASEGURAR( dicc.Obtener( "Teléfono fijo" ), "011-4444-4444" );
	ASEGURAR( dicc.Obtener( "Teléfono móvil" ), "011-15-4444-4444" );
	ASEGURAR( dicc.Obtener( "Nomb" ), "Pinocho!!" );
}

void DiccTrieReemplazar( std::ostream & ) {
	DiccTrie<std::string> dicc;
	DiccTrieCrear( dicc );
	ASEGURAR( dicc.Obtener( "Nombre" ), "Pinocho" );
	dicc.Definir( "Nombre", "Batman" );
	ASEGURAR( dicc.Obtener( "Nombre" ), "Batman" );
}

/* ArbolCategorias */
ArbolCategorias *ArbolCategoriasCrear() {
	ArbolCategorias *ac = new ArbolCategorias( "raiz" );
	ac->Agregar( "raiz", "nivel1.1" );
	ac->Agregar( "raiz", "nivel1.2" );
	ac->Agregar( "raiz", "nivel1.3" );
	ac->Agregar( "nivel1.1", "nivel2.1" );
	ac->Agregar( "nivel1.1", "nivel2.2" );
	ac->Agregar( "nivel1.3", "nivel2.3" );
	ac->Agregar( "nivel2.2", "nivel3.1" );
	ac->Agregar( "nivel2.2", "nivel3.2" );
	ac->Agregar( "nivel2.2", "nivel3.3" );
	return ac;
}

void ArbolCategoriasConstructor( std::ostream & ) {
	ArbolCategorias ac( "raiz" );
}

void ArbolCategoriasAgregar( std::ostream & ) {
	ArbolCategorias *ac = ArbolCategoriasCrear();
	delete ac;
}

void ArbolCategoriasRaiz( std::ostream & ) {
	ArbolCategorias *ac = ArbolCategoriasCrear();
	ASEGURAR( ac->Raiz(), "raiz" );
	delete ac;
}

void ArbolCategoriasPadre( std::ostream & ) {
	ArbolCategorias *ac = ArbolCategoriasCrear();
	ASEGURAR( ac->Padre( "nivel1.1" ), "raiz" );
	ASEGURAR( ac->Padre( "nivel1.2" ), "raiz" );
	ASEGURAR( ac->Padre( "nivel1.3" ), "raiz" );
	ASEGURAR( ac->Padre( "nivel2.1" ), "nivel1.1" );
	ASEGURAR( ac->Padre( "nivel2.2" ), "nivel1.1" );
	ASEGURAR( ac->Padre( "nivel2.3" ), "nivel1.3" );
	ASEGURAR( ac->Padre( "nivel3.1" ), "nivel2.2" );
	ASEGURAR( ac->Padre( "nivel3.2" ), "nivel2.2" );
	ASEGURAR( ac->Padre( "nivel3.3" ), "nivel2.2" );
	delete ac;
}

void ArbolCategoriasId( std::ostream & ) {
	ArbolCategorias *ac = ArbolCategoriasCrear();
	ASEGURAR( ac->Id( "raiz" ), 1 );
	ASEGURAR( ac->Id( "nivel1.1" ), 2 );
	ASEGURAR( ac->Id( "nivel1.2" ), 3 );
	ASEGURAR( ac->Id( "nivel1.3" ), 4 );
	ASEGURAR( ac->Id( "nivel2.1" ), 5 );
	ASEGURAR( ac->Id( "nivel2.2" ), 6 );
	ASEGURAR( ac->Id( "nivel2.3" ), 7 );
	ASEGURAR( ac->Id( "nivel3.1" ), 8 );
	ASEGURAR( ac->Id( "nivel3.2" ), 9 );
	ASEGURAR( ac->Id( "nivel3.3" ), 10 );
	delete ac;
}

void ArbolCategoriasCantidad( std::ostream & ) {
	ArbolCategorias *ac = ArbolCategoriasCrear();
	ASEGURAR( ac->CantCategorias(), 10 )
	ac->Agregar( "raiz", "asdf" );
	ASEGURAR( ac->CantCategorias(), 11 )
	ac->Agregar( "asdf", "bsdf" );
	ASEGURAR( ac->CantCategorias(), 12 )
	ac->Agregar( "nivel3.3", "pepepe" );
	ASEGURAR( ac->CantCategorias(), 13 )
	delete ac;
}

/* Arbol Categorias Iterador */
void ArbolCategoriasIteradorCrear( std::ostream & ) {
	ArbolCategorias *ac = ArbolCategoriasCrear();
	ArbolCategorias::Iterador *it = new ArbolCategorias::Iterador( *ac, "raiz" );
	delete it;
	delete ac;
}

struct ElementoAC {
	std::string cat, padre;
	Nat id, padreId;
	bool tienePadre;
};

void ArbolCategoriasIteradorIterar( std::ostream & ) {
	ArbolCategorias *ac = ArbolCategoriasCrear();
	ArbolCategorias::Iterador *it = new ArbolCategorias::Iterador( *ac );
	bool visitado[3] = { false, false, false };
	ElementoAC hijosRaiz[3] = {
		{ "nivel1.1", "raiz", 2, 1, true },
		{ "nivel1.2", "raiz", 3, 1, true },
		{ "nivel1.3", "raiz", 4, 1, true },
	};

	for ( int i = 0; i < 3; i++ ) {
		AFIRMAR( it->HayMas() )
		for ( int j = 0; j < 3; j++ ) {
			ArbolCategorias::Iterador::ItElem e = it->Siguiente();
			if ( e.cat == hijosRaiz[j].cat ) {
				ASEGURAR( e.id, hijosRaiz[j].id );
				ASEGURAR( e.tienePadre, hijosRaiz[j].tienePadre )
				ASEGURAR( e.padre, hijosRaiz[j].padre );
				ASEGURAR( e.padreId, hijosRaiz[j].padreId );
				NEGAR( visitado[j] );
				visitado[j] = true;
			}
		}
		it->Avanzar();
	}

	for ( int i = 0; i < 3; i++ )
		AFIRMAR( visitado[i] );

	NEGAR( it->HayMas() );
	delete it;

	it = new ArbolCategorias::Iterador( *ac, "raiz" );
	for ( int i = 0; i < 3; i++ ) visitado[i] = false;
	for ( int i = 0; i < 3; i++ ) {
		AFIRMAR( it->HayMas() )
		for ( int j = 0; j < 3; j++ ) {
			ArbolCategorias::Iterador::ItElem e = it->Siguiente();
			if ( e.cat == hijosRaiz[j].cat ) {
				ASEGURAR( e.id, hijosRaiz[j].id );
				ASEGURAR( e.tienePadre, hijosRaiz[j].tienePadre )
				ASEGURAR( e.padre, hijosRaiz[j].padre );
				ASEGURAR( e.padreId, hijosRaiz[j].padreId );
				NEGAR( visitado[j] );
				visitado[j] = true;
			}
		}
		it->Avanzar();
	}

	for ( int i = 0; i < 3; i++ )
		AFIRMAR( visitado[i] );

	NEGAR( it->HayMas() );
	delete it;

	ElementoAC hijos[2] = {
		{ "nivel2.1", "nivel1.1", 5, 2, true },
		{ "nivel2.2", "nivel1.1", 6, 2, true }
	};

	it = new ArbolCategorias::Iterador( *ac, ArbolCategorias::Iterador( *ac ) );
	for ( int i = 0; i < 2; i++ ) visitado[i] = false;
	for ( int i = 0; i < 2; i++ ) {
		AFIRMAR( it->HayMas() )
		for ( int j = 0; j < 2; j++ ) {
			ArbolCategorias::Iterador::ItElem e = it->Siguiente();
			if ( e.cat == hijos[j].cat ) {
				ASEGURAR( e.id, hijos[j].id );
				ASEGURAR( e.tienePadre, hijos[j].tienePadre )
				ASEGURAR( e.padre, hijos[j].padre );
				ASEGURAR( e.padreId, hijos[j].padreId );
				NEGAR( visitado[j] );
				visitado[j] = true;
			}
		}
		it->Avanzar();
	}

	for ( int i = 0; i < 2; i++ )
		AFIRMAR( visitado[i] );

	NEGAR( it->HayMas() );
	delete it;

	delete ac;
}

/* LinkLinkIt */
LinkLinkIt *LinkLinkItCrear( ArbolCategorias *&ac ) {
	ac = ArbolCategoriasCrear();
	LinkLinkIt *s = new LinkLinkIt( *ac );
	s->NuevoLink( "link1", "raiz" );
	s->NuevoLink( "link2", "nivel1.1" );
	s->NuevoLink( "link3", "nivel1.1" );
	s->NuevoLink( "link4", "nivel2.2" );
	s->NuevoLink( "link5", "nivel2.2" );
	s->NuevoLink( "link6", "nivel3.2" );
	s->NuevoLink( "link7", "nivel3.2" );
	s->NuevoLink( "link8", "nivel3.1" );
	s->NuevoLink( "link9", "nivel3.3" );
	return s;
}

void LinkLinkItAcceder( LinkLinkIt *s ) {
	s->Acceso( "link1", 1 );
	s->Acceso( "link1", 1 );
	s->Acceso( "link3", 1 );
	s->Acceso( "link7", 1 );
	s->Acceso( "link2", 2 );
	s->Acceso( "link2", 2 );
	s->Acceso( "link6", 2 );
	s->Acceso( "link1", 3 );
	s->Acceso( "link2", 3 );
	s->Acceso( "link4", 3 );
	s->Acceso( "link6", 3 );
	s->Acceso( "link2", 4 );
	s->Acceso( "link3", 4 );
	s->Acceso( "link4", 4 );
	s->Acceso( "link5", 4 );
	s->Acceso( "link8", 4 );
	s->Acceso( "link4", 5 );
	s->Acceso( "link9", 5 );
}

void LinkLinkItConstructor( std::ostream & ) {
	ArbolCategorias *ac = ArbolCategoriasCrear();
	LinkLinkIt *s = new LinkLinkIt( *ac );
	delete s;
	delete ac;
}

void LinkLinkItAgregar( std::ostream & ) {
	ArbolCategorias *ac;
	LinkLinkIt *s = LinkLinkItCrear( ac );
	delete s;
	delete ac;
}

void LinkLinkItAcceso( std::ostream & ) {
	ArbolCategorias *ac;
	LinkLinkIt *s = LinkLinkItCrear( ac );
	LinkLinkItAcceder( s );
	delete s;
	delete ac;
}

void LinkLinkItCantLinks( std::ostream & ) {
	ArbolCategorias *ac;
	LinkLinkIt *s = LinkLinkItCrear( ac );
	ASEGURAR( s->CantLinks( "raiz" ), 9 );
	ASEGURAR( s->CantLinks( "nivel1.1" ), 8 );
	ASEGURAR( s->CantLinks( "nivel1.2" ), 0 );
	ASEGURAR( s->CantLinks( "nivel1.3" ), 0 );
	ASEGURAR( s->CantLinks( "nivel2.1" ), 0 );
	ASEGURAR( s->CantLinks( "nivel2.2" ), 6 );
	ASEGURAR( s->CantLinks( "nivel2.3" ), 0 );
	ASEGURAR( s->CantLinks( "nivel3.1" ), 1 );
	ASEGURAR( s->CantLinks( "nivel3.2" ), 2 );
	ASEGURAR( s->CantLinks( "nivel3.3" ), 1 );
	delete s;
	delete ac;
}

/* LinkLinkIt::Iterador */
void LinkLinkItIteradorCrear( std::ostream & ) {
	ArbolCategorias *ac;
	LinkLinkIt *s = LinkLinkItCrear( ac );
	LinkLinkIt::Iterador *it;
	LinkLinkItAcceder( s );
	
	it = new LinkLinkIt::Iterador( *s, "raiz" ); delete it;
	it = new LinkLinkIt::Iterador( *s, "nivel1.1" ); delete it;
	it = new LinkLinkIt::Iterador( *s, "nivel1.1" ); delete it;
	it = new LinkLinkIt::Iterador( *s, "nivel2.1" ); delete it;
	it = new LinkLinkIt::Iterador( *s, "nivel2.2" ); delete it;
	it = new LinkLinkIt::Iterador( *s, "nivel2.3" ); delete it;
	it = new LinkLinkIt::Iterador( *s, "nivel3.1" ); delete it;
	it = new LinkLinkIt::Iterador( *s, "nivel3.2" ); delete it;
	it = new LinkLinkIt::Iterador( *s, "nivel3.3" ); delete it;

	delete s;
	delete ac;
}

void LinkLinkItIterarRaiz( LinkLinkIt *s ) {
	LinkLinkIt::Iterador it( *s, "raiz" );

	bool link1Visitado = false, link2Visitado = false;
	for ( int i = 0; i < 2; i++ ) {
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		if ( !link1Visitado && e.link == "link1" ) {
			ASEGURAR( e.categoria, "raiz" );
			ASEGURAR( e.accesosRecientes, 3 );
			link1Visitado = true;
		} else if ( !link2Visitado && e.link == "link2" ) {
			ASEGURAR( e.categoria, "nivel1.1" );
			ASEGURAR( e.accesosRecientes, 3 );
			link2Visitado = true;
		}
		it.Avanzar();
	}
	AFIRMAR( link1Visitado );
	AFIRMAR( link2Visitado );

	{
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		ASEGURAR( e.link, "link6" );
		ASEGURAR( e.categoria, "nivel3.2" );
		ASEGURAR( e.accesosRecientes, 2 );
		it.Avanzar();
	}

	bool link4Visitado = false, link7Visitado = false;
	for ( int i = 0; i < 2; i++ ) {
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		if ( !link4Visitado && e.link == "link4" ) {
			ASEGURAR( e.categoria, "nivel2.2" );
			ASEGURAR( e.accesosRecientes, 1 );
			link4Visitado = true;
		} else if ( !link7Visitado && e.link == "link7" ) {
			ASEGURAR( e.categoria, "nivel3.2" );
			ASEGURAR( e.accesosRecientes, 1 );
			link7Visitado = true;
		}
		it.Avanzar();
	}
	AFIRMAR( link4Visitado );
	AFIRMAR( link7Visitado );


	bool link3Visitado = false,
		link5Visitado = false,
		link8Visitado = false,
		link9Visitado = false;
	
	for ( int i = 0; i < 4; i++ ) {
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();

		if ( !link3Visitado && e.link == "link3" ) {
			ASEGURAR( e.categoria, "nivel1.1" );
			ASEGURAR( e.accesosRecientes, 0 );
			link3Visitado = true;
		} else if ( !link5Visitado && e.link == "link5" ) {
			ASEGURAR( e.categoria, "nivel2.2" );
			ASEGURAR( e.accesosRecientes, 0 );
			link5Visitado = true;
		} else if ( !link8Visitado && e.link == "link8" ) {
			ASEGURAR( e.categoria, "nivel3.1" );
			ASEGURAR( e.accesosRecientes, 0 );
			link8Visitado = true;
		} else if ( !link9Visitado && e.link == "link9" ) {
			ASEGURAR( e.categoria, "nivel3.3" );
			ASEGURAR( e.accesosRecientes, 0 );
			link9Visitado = true;
		}
		it.Avanzar();
	}
	AFIRMAR( link3Visitado );
	AFIRMAR( link5Visitado );
	AFIRMAR( link8Visitado );
	AFIRMAR( link9Visitado );
}

void LinkLinkItIterarNivel11( LinkLinkIt *s ) {
	LinkLinkIt::Iterador it( *s, "nivel1.1" );
	
	// Los accesos recientes para la categoría "nivel1.1" serían:
	// [link2 -> 4, 
	// {link6 -> 2, link4 -> 2}, 
	// {link3 -> 1, link5 -> 1, link8 -> 1},
	// {link7 -> 0, link9 -> 0}]
	
	// Sólo un link con 4 accesos recientes
	{
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		ASEGURAR( e.link, "link2" );
		ASEGURAR( e.categoria, "nivel1.1" );
		ASEGURAR( e.accesosRecientes, 4 );
		it.Avanzar();
	}
	
	// Tenemos 2 links con 2 accesos recientes
	bool link4Visitado = false, link6Visitado = false;
	for ( int i = 0; i < 2; i++ ) {
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		if ( !link4Visitado && e.link == "link4" ) {
			ASEGURAR( e.categoria, "nivel2.2" );
			ASEGURAR( e.accesosRecientes, 2 );
			link4Visitado = true;
		} else if ( !link6Visitado && e.link == "link6" ) {
			ASEGURAR( e.categoria, "nivel3.2" );
			ASEGURAR( e.accesosRecientes, 2 );
			link6Visitado = true;
		}
		it.Avanzar();
	}
	AFIRMAR( link4Visitado );
	AFIRMAR( link6Visitado );
	
	// Tenemos 3 links con 1 acceso reciente
	bool link3Visitado = false, link5Visitado = false, link8Visitado = false;
	for ( int i = 0; i < 3; i++ ) {
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		if ( !link3Visitado && e.link == "link3" ) {
			ASEGURAR( e.categoria, "nivel1.1" );
			ASEGURAR( e.accesosRecientes, 1 );
			link3Visitado = true;
		} else if ( !link5Visitado && e.link == "link5" ) {
			ASEGURAR( e.categoria, "nivel2.2" );
			ASEGURAR( e.accesosRecientes, 1 );
			link5Visitado = true;
		} else if ( !link8Visitado && e.link == "link8" ) {
			ASEGURAR( e.categoria, "nivel3.1" );
			ASEGURAR( e.accesosRecientes, 1 );
			link8Visitado = true;
		}
		
		it.Avanzar();
	}
	AFIRMAR( link3Visitado );
	AFIRMAR( link5Visitado );
	AFIRMAR( link8Visitado );
	
	// Tenemos 2 links con 0 accesos recientes
	bool link7Visitado = false, link9Visitado = false;
	for ( int i = 0; i < 2; i++ ) {
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		if ( !link7Visitado && e.link == "link7" ) {
			ASEGURAR( e.categoria, "nivel3.2" );
			ASEGURAR( e.accesosRecientes, 0 );
			link7Visitado = true;
		} else if ( !link9Visitado && e.link == "link9" ) {
			ASEGURAR( e.categoria, "nivel3.3" );
			ASEGURAR( e.accesosRecientes, 0 );
			link9Visitado = true;
		}
		it.Avanzar();
	}
	AFIRMAR( link7Visitado );
	AFIRMAR( link9Visitado );
	
	// Ya no hay más links para revisar
	NEGAR( it.HayMas() );
}

void LinkLinkItIterarNivel12( LinkLinkIt *s ) {
	LinkLinkIt::Iterador it( *s, "nivel1.2" );
	
	// La categoría "nivel1.2" no tiene links
	
	// Ya no hay más links para revisar
	NEGAR( it.HayMas() );
}

void LinkLinkItIterarNivel13( LinkLinkIt *s ) {
	LinkLinkIt::Iterador it( *s, "nivel1.3" );
	
	// La categoría "nivel1.3" no tiene links
	
	// Ya no hay más links para revisar
	NEGAR( it.HayMas() );
}

void LinkLinkItIterarNivel21( LinkLinkIt *s ) {
	LinkLinkIt::Iterador it( *s, "nivel2.1" );
	
	// La categoría "nivel2.1" no tiene links
	
	// Ya no hay más links para revisar
	NEGAR( it.HayMas() );
}

void LinkLinkItIterarNivel22( LinkLinkIt *s ) {
	LinkLinkIt::Iterador it( *s, "nivel2.2" );
	
	// Los accesos recientes para la categoría "nivel2.2" serían:
	// [link4 -> 3, 
	// {link5 -> 1, link6 -> 1, link8 -> 1, link9 -> 1},
	// link7 -> 0]
	
	// Sólo un link con 3 accesos recientes
	{
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		ASEGURAR( e.link, "link4" );
		ASEGURAR( e.categoria, "nivel2.2" );
		ASEGURAR( e.accesosRecientes, 3 );
		it.Avanzar();
	}
	
	// Tenemos 4 links con 1 acceso reciente
	bool link5Visitado = false, link6Visitado = false, link8Visitado = false, link9Visitado = false;
	for ( int i = 0; i < 4; i++ ) {
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		if ( !link5Visitado && e.link == "link5" ) {
			ASEGURAR( e.categoria, "nivel2.2" );
			ASEGURAR( e.accesosRecientes, 1 );
			link5Visitado = true;
		} else if ( !link6Visitado && e.link == "link6" ) {
			ASEGURAR( e.categoria, "nivel3.2" );
			ASEGURAR( e.accesosRecientes, 1 );
			link6Visitado = true;
		} else if ( !link8Visitado && e.link == "link8" ) {
			ASEGURAR( e.categoria, "nivel3.1" );
			ASEGURAR( e.accesosRecientes, 1 );
			link8Visitado = true;
		} else if ( !link9Visitado && e.link == "link9" ) {
			ASEGURAR( e.categoria, "nivel3.3" );
			ASEGURAR( e.accesosRecientes, 1 );
			link9Visitado = true;
		}
		
		it.Avanzar();
	}
	AFIRMAR( link5Visitado );
	AFIRMAR( link6Visitado );
	AFIRMAR( link8Visitado );
	AFIRMAR( link9Visitado );
	
	// Sólo un link con 0 accesos recientes
	{
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		ASEGURAR( e.link, "link7" );
		ASEGURAR( e.categoria, "nivel3.2" );
		ASEGURAR( e.accesosRecientes, 0 );
		it.Avanzar();
	}
	
	// Ya no hay más links para revisar
	NEGAR( it.HayMas() );
}

void LinkLinkItIterarNivel23( LinkLinkIt *s ) {
	LinkLinkIt::Iterador it( *s, "nivel2.3" );
	
	// La categoría "nivel2.3" no tiene links
	
	// Ya no hay más links para revisar
	NEGAR( it.HayMas() );
}

void LinkLinkItIterarNivel31( LinkLinkIt *s ) {
	LinkLinkIt::Iterador it( *s, "nivel3.1" );
	
	// Los accesos recientes para la categoría "nivel3.1" serían:
	// [link8 -> 1]
	
	// Sólo un link con 1 acceso reciente
	{
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		ASEGURAR( e.link, "link8" );
		ASEGURAR( e.categoria, "nivel3.1" );
		ASEGURAR( e.accesosRecientes, 1 );
		it.Avanzar();
	}
	
	// Ya no hay más links para revisar
	NEGAR( it.HayMas() );
}

void LinkLinkItIterarNivel32( LinkLinkIt *s ) {
	LinkLinkIt::Iterador it( *s, "nivel3.2" );
	
	// Los accesos recientes para la categoría "nivel3.2" serían:
	// [link6 -> 2, link7 -> 1]
	
	// Sólo un link con 2 accesos recientes
	{
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		ASEGURAR( e.link, "link6" );
		ASEGURAR( e.categoria, "nivel3.2" );
		ASEGURAR( e.accesosRecientes, 2 );
		it.Avanzar();
	}
	
	// Sólo un link con 1 acceso reciente
	{
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		ASEGURAR( e.link, "link7" );
		ASEGURAR( e.categoria, "nivel3.2" );
		ASEGURAR( e.accesosRecientes, 1 );
		it.Avanzar();
	}
	
	// Ya no hay más links para revisar
	NEGAR( it.HayMas() );
}

void LinkLinkItIterarNivel33( LinkLinkIt *s ) {
	LinkLinkIt::Iterador it( *s, "nivel3.3" );
	
	// Los accesos recientes para la categoría "nivel3.3" serían:
	// [link9 -> 1]
	
	// Sólo un link con 1 acceso reciente
	{
		AFIRMAR( it.HayMas() );
		LinkLinkIt::Iterador::ItElem e = it.Siguiente();
		ASEGURAR( e.link, "link9" );
		ASEGURAR( e.categoria, "nivel3.3" );
		ASEGURAR( e.accesosRecientes, 1 );
		it.Avanzar();
	}
	
	// Ya no hay más links para revisar
	NEGAR( it.HayMas() );
}

void LinkLinkItIteradorIterar( std::ostream & ) {
	ArbolCategorias *ac;
	LinkLinkIt *s = LinkLinkItCrear( ac );
	LinkLinkItAcceder( s );

	LinkLinkItIterarRaiz( s );
	LinkLinkItIterarNivel11( s );
	LinkLinkItIterarNivel12( s );
	LinkLinkItIterarNivel13( s );
	LinkLinkItIterarNivel21( s );
	LinkLinkItIterarNivel22( s );
	LinkLinkItIterarNivel23( s );
	LinkLinkItIterarNivel31( s );
	LinkLinkItIterarNivel32( s );
	LinkLinkItIterarNivel33( s );

	delete s;
	delete ac;
}


int main( void ) {
	aed2::Tester *test = aed2::Tester::Instance();

	/* DiccTrie */
	test->NuevoSet( "DiccTrie" );
	test->UsarSet( "DiccTrie" );
	test->Declarar( "Constructor", DiccTrieConstructor );
	test->Declarar( "Destructor", DiccTrieDestructor );
	test->Declarar( "Definir", DiccTrieDefinir );
	test->Declarar( "Definido?", DiccTrieDefinido );
	test->Declarar( "Obtener", DiccTrieObtener );
	test->Declarar( "Reemplazar", DiccTrieReemplazar );

	/* ArbolCategorias */
	test->NuevoSet( "ArbolCategorias" );
	test->UsarSet( "ArbolCategorias" );
	test->Declarar( "Constructor", ArbolCategoriasConstructor );
	test->Declarar( "Agregar", ArbolCategoriasAgregar );
	test->Declarar( "Raiz", ArbolCategoriasRaiz );
	test->Declarar( "Padre", ArbolCategoriasPadre );
	test->Declarar( "Id", ArbolCategoriasId );
	test->Declarar( "CantCategorias", ArbolCategoriasCantidad );

	test->NuevoSet( "ArbolCategoriasIterador" );
	test->UsarSet( "ArbolCategoriasIterador" );
	test->Declarar( "Constructor", ArbolCategoriasIteradorCrear );
	test->Declarar( "Iterador", ArbolCategoriasIteradorIterar );

	/* LinkLinkIt */
	test->NuevoSet( "LinkLinkIt" );
	test->UsarSet( "LinkLinkIt" );
	test->Declarar( "Constructor", LinkLinkItConstructor );
	test->Declarar( "Agregar", LinkLinkItAgregar );
	test->Declarar( "Acceso", LinkLinkItAcceso );
	test->Declarar( "CantLinks", LinkLinkItCantLinks );

	test->NuevoSet( "LinkLinkItIterador" );
	test->UsarSet( "LinkLinkItIterador" );
	test->Declarar( "Constructor", LinkLinkItIteradorCrear );
	test->Declarar( "Iterar", LinkLinkItIteradorIterar );

	test->setOutput( cout );
	test->setVerborragia( 2 );
	test->EjecutarTodos();

	return 0;
}
